﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using STSdb.Data;
using System.Threading;

using STSdb.MTL.Utils.Serialization;

namespace STSdb.MTL
{
    /// <summary>
    /// Holds MTL variant of schemes for STSdb Tables
    /// </summary>
    public class StsTablesSchema
    {
        TransactionsCoordinator _transactionsCoordinator = null;
        public CompareUserTableSchema UserTableSchemaComparer = new CompareUserTableSchema();

        public StsTablesSchema(TransactionsCoordinator transactionsCoordinator)
        {
            this._transactionsCoordinator = transactionsCoordinator;
        }

        //////////////////////////////////////////   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   TABLES LIKE THIS:
        //Must have their one lock, then we don't to create different locators and use only one.
                
        /// <summary>
        /// Key holds user defined table name, Value holds serialized object UserTableSchema
        /// </summary>
        XTable<string, string> @sys_sts_tables_schemes = null;
        string @sys_sts_tables_schemes_stringName = "@sys_sts_tables_schemes";

        object table_lock = new object();
        ReaderWriterLockSlim _sync = new ReaderWriterLockSlim();


        #region "Init"

        /// <summary>
        /// Init is called by SchemaCoordinator after initialization, safe.
        /// After initialization @sys_sts_tables_schemes will hold reference to this table.
        /// All other inserts and deletes into STS schema must come via SyncRoot
        /// </summary>
        public void Init()
        {
            try
            {
                Locator l = new Locator(@sys_sts_tables_schemes_stringName);
                
                if (this._transactionsCoordinator.StorageEngine.Scheme.Exists(l))
                {
                    @sys_sts_tables_schemes = this._transactionsCoordinator.StorageEngine.Scheme.OpenXTable<string, string>(new Locator(@sys_sts_tables_schemes_stringName));

                    @sys_sts_tables_schemes.KeyMap = new StringKeyMap(150,SortOrderMap.Ascending);
                    //As locator has
                    //@sys_sts_tables_schemes.KeyMap = new StringKeyMap(0,SortOrderMap.None);
                }
                else
                {
                    @sys_sts_tables_schemes = this._transactionsCoordinator.StorageEngine.Scheme.CreateXTable<string, string>(new Locator(@sys_sts_tables_schemes_stringName));

                    @sys_sts_tables_schemes.KeyMap = new StringKeyMap(150, SortOrderMap.Ascending);
                    //As locator has
                    //@sys_sts_tables_schemes.KeyMap = new StringKeyMap(0, SortOrderMap.None);
                    this._transactionsCoordinator.StorageEngine.Scheme.Commit();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #endregion

        /// <summary>
        /// Comparator for user table schema, used in the place where we need to compare objects UserTableSchema.
        /// here representative UserTableSchemaComparer
        /// </summary>
        public class CompareUserTableSchema : IEqualityComparer<UserTableSchema>
        {

            public bool Equals(UserTableSchema x, UserTableSchema y)
            {
                return x.TableName.Equals(y.TableName);
            }

            public int GetHashCode(UserTableSchema obj)
            {
                return obj.CompactedInfoPointer.GetHashCode();
            }
        }


        ///// <summary>
        ///// User's table name can be switch after compacting procedure, from userTableName to @sys_etn_userTableName/2 and back. 
        ///// Compacter decides which table is actual now.
        ///// </summary>
        ///// <param name="originalUserTableName"></param>
        ///// <returns></returns>
        //public string GetUsersTableName(string originalUserTableName)
        //{
        //    string ser_UserTableSchema = String.Empty;
        //    UserTableSchema uts = null;
        //    bool couldGetValue = false;

        //    _sync.EnterReadLock();
        //    try
        //    {
        //        couldGetValue = tblSch.TryGetValue(originalUserTableName, out ser_UserTableSchema);
        //    }
        //    catch (System.Exception ex)
        //    {
        //        throw ex;
        //    }
        //    finally
        //    {
        //        _sync.ExitReadLock();
        //    }


        //    if (couldGetValue)
        //    {
        //        uts = ser_UserTableSchema.DeserializeXml<UserTableSchema>();

        //        //calling compacter about current name
        //        return this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.GetUsersTableName(originalUserTableName, uts.CompactedInfoPointer);
        //    }
        //    else
        //    {
        //        //returning original name
        //        return originalUserTableName;
        //    }
        //}


        /// <summary>
        /// Can be used by SchemaCoordinator to retrieve original schema names, counting all system nuances
        /// </summary>
        /// <param name="schemaTableName"></param>
        /// <returns></returns>
        public string GetOriginalUserTableNameFromSchema(string schemaTableName)
        {
            if(schemaTableName.StartsWith("@sys_etn_"))
            {
                return schemaTableName.Substring(9, schemaTableName.Length - 9 - 2);
            }

            return schemaTableName;
        }


        /// <summary>
        /// User's table name can be switch after compacting procedure, from userTableName to @sys_etn_userTableName/2 and back. 
        /// Compacter decides which table is actual now.
        /// </summary>
        /// <param name="originalUserTableName"></param>
        /// <returns></returns>
        public string GetUsersTableName(string originalUserTableName)
        {
            string ser_UserTableSchema = String.Empty;
            UserTableSchema uts = null;
            bool couldGetValue = false;


            lock (table_lock)
            {
                try
                {
                    couldGetValue = @sys_sts_tables_schemes.TryGet(originalUserTableName, out ser_UserTableSchema);
                    if (couldGetValue)
                    {
                        uts = ser_UserTableSchema.DeserializeXml<UserTableSchema>();
                        return this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.GetUsersTableName(originalUserTableName, uts.CompactedInfoPointer);
                    }
                    else
                    {
                        return originalUserTableName;
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            

            //using (var tran = this._transactionsCoordinator.GetTransaction())
            //{
            //    try
            //    {
            //        var tr = tran.Select<string, string>(@sys_sts_tables_schemes_stringName, eSelectMode.READ_SYNCHRO, originalUserTableName);
            //        if (!tr.Exists)
            //            return originalUserTableName;
            //        uts = tr.Value.DeserializeXml<UserTableSchema>();
            //        //calling compacter about current name
            //        return this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.GetUsersTableName(originalUserTableName, uts.CompactedInfoPointer);
            //    }
            //    catch (Exception ex)
            //    {                    
            //        throw ex;
            //    }                
            //}




            //_sync.EnterReadLock();
            //try
            //{
            //    couldGetValue = @sys_sts_tables_schemes.TryGet(originalUserTableName, out ser_UserTableSchema);
            //}
            //catch (System.Exception ex)
            //{
            //    throw ex;
            //}
            //finally
            //{
            //    _sync.ExitReadLock();
            //}


            //if (couldGetValue)
            //{
            //    uts = ser_UserTableSchema.DeserializeXml<UserTableSchema>();

            //    //calling compacter about current name
            //    return this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.GetUsersTableName(originalUserTableName, uts.CompactedInfoPointer);
            //}
            //else
            //{
            //    //returning original name
            //    return originalUserTableName;
            //}
        }

        /// <summary>
        /// Deletes table from Schema
        /// </summary>
        /// <param name="originalUserTableName"></param>
        public void DeleteTable(string originalUserTableName)
        {
            string ser_UserTableSchema=String.Empty;
            UserTableSchema uts = null;
            bool couldGetValue = false;

            lock (table_lock)
            {
                try
                {
                    couldGetValue = @sys_sts_tables_schemes.TryGet(originalUserTableName, out ser_UserTableSchema);
                    if (couldGetValue)
                    {
                        uts = ser_UserTableSchema.DeserializeXml<UserTableSchema>();
                        this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.DeleteTable(uts.CompactedInfoPointer);
                    }                  
                }
                catch (Exception ex)
                {                    
                    throw ex;
                }
                
            }

            //using (var tran = this._transactionsCoordinator.GetTransaction())
            //{
            //    try
            //    {
            //        var tr = tran.Select<string, string>(@sys_sts_tables_schemes_stringName, eSelectMode.READ_SYNCHRO, originalUserTableName);
            //        if (tr.Exists)
            //        {
            //            uts = tr.Value.DeserializeXml<UserTableSchema>();
            //            //Setting Up Compacter to the first active table
            //            this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.DeleteTable(uts.CompactedInfoPointer);
            //        }
            //    }
            //    catch (Exception ex)
            //    {
            //        throw ex;
            //    }
            //}


            //bool couldGetValue = false;

            ////Setting Up Compacter to the first active table
            //_sync.EnterReadLock();
            //try
            //{
            //    couldGetValue = @sys_sts_tables_schemes.TryGet(originalUserTableName, out ser_UserTableSchema);
            //}
            //catch (System.Exception ex)
            //{
            //    throw ex;
            //}
            //finally
            //{
            //    _sync.ExitReadLock();
            //}


            //if (couldGetValue)
            //{
            //    uts = ser_UserTableSchema.DeserializeXml<UserTableSchema>();
            //    this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.DeleteTable(uts.CompactedInfoPointer);
            //}
            ////-----------------------------------------------
        }









        //Dictionary<string, string> tblSch = new Dictionary<string, string>();


        ///// <summary>
        ///// Calls compacting routine.
        ///// </summary>
        ///// <param name="originalUserTableName"></param>
        //public void CompactTable(string originalUserTableName)
        //{
        //    if (!StsTablesCompactor.COMPACTOR_IS_ACTIVATED)
        //        return;

        //    bool couldGetValue = false;
        //    string ser_UserTableSchema = String.Empty;
        //    UserTableSchema uts = null;


        //    _sync.EnterUpgradeableReadLock();
        //    try
        //    {
        //        couldGetValue = tblSch.TryGetValue(originalUserTableName, out ser_UserTableSchema);
        //        //couldGetValue = @sys_sts_tables_schemes.TryGet(originalUserTableName, out ser_UserTableSchema);

        //        //return;

        //        if (!couldGetValue)
        //        {
        //            _sync.EnterWriteLock();
        //            try
        //            {
        //                uts = new UserTableSchema();
        //                uts.CompactedInfoPointer = this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.NewTableCreatedInSchema();

        //                //@sys_sts_tables_schemes[originalUserTableName] = uts.SerializeXml();
        //                tblSch.Add(originalUserTableName, uts.SerializeXml());

        //                //lock (this._transactionsCoordinator._SYNC_COMMIT)
        //                //{
        //                //    @sys_sts_tables_schemes.Commit();
        //                //}
        //            }
        //            catch (System.Exception ex)
        //            {
        //                throw ex;
        //            }
        //            finally
        //            {
        //                _sync.ExitWriteLock();
        //            }
        //        }
        //        else
        //        {
        //            uts = ser_UserTableSchema.DeserializeXml<UserTableSchema>();
        //        }

        //        uts.TableName = originalUserTableName;
        //        this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.CompactTable(uts);
               

        //    }
        //    catch (System.Exception ex)
        //    {
        //        TableCompactingResult cr = new TableCompactingResult();
        //        cr.RealTableName = originalUserTableName;
        //        cr.CompacterException = ex;
        //        this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.TablesCompactingResultHasArrived(cr);
        //        return;
        //    }
        //    finally
        //    {
        //        _sync.ExitUpgradeableReadLock();
        //    }
        //}




        /// <summary>
        /// Calls compacting routine.
        /// </summary>
        /// <param name="originalUserTableName"></param>
        public void CompactTable(string originalUserTableName)
        {
            if (!StsTablesCompactor.COMPACTOR_IS_ACTIVATED)
                return;

            bool couldGetValue = false;
            string ser_UserTableSchema = String.Empty;
            UserTableSchema uts = null;

            lock (table_lock)
            {
                try
                {
                    couldGetValue = @sys_sts_tables_schemes.TryGet(originalUserTableName, out ser_UserTableSchema);
                    if (!couldGetValue)
                    {
                        uts = new UserTableSchema();
                        uts.CompactedInfoPointer = this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.NewTableCreatedInSchema();
                        @sys_sts_tables_schemes[originalUserTableName] = uts.SerializeXml();

                        //lock (this._transactionsCoordinator._SYNC_COMMIT)       
                        //{
                            @sys_sts_tables_schemes.Commit();
                        //}
                    }
                    else
                    {
                        uts = ser_UserTableSchema.DeserializeXml<UserTableSchema>();
                    }

                }
                catch (Exception ex)
                {
                    throw ex;
                }

            }


            uts.TableName = originalUserTableName;
            this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.CompactTable(uts);

            //_sync.EnterUpgradeableReadLock();
            //try
            //{

            //    couldGetValue = @sys_sts_tables_schemes.TryGet(originalUserTableName, out ser_UserTableSchema);

            //    //return;

            //    if (!couldGetValue)
            //    {
            //        _sync.EnterWriteLock();
            //        try
            //        {
            //            uts = new UserTableSchema();
            //            uts.CompactedInfoPointer = this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.NewTableCreatedInSchema();

            //            @sys_sts_tables_schemes[originalUserTableName] = uts.SerializeXml();

            //            lock (this._transactionsCoordinator._SYNC_COMMIT)
            //            {
            //                @sys_sts_tables_schemes.Commit();
            //                @sys_sts_tables_schemes = this._transactionsCoordinator.StorageEngine.Scheme.OpenXTable<string, string>(new Locator(@sys_sts_tables_schemes_stringName));
            //            }
            //        }
            //        catch (System.Exception ex)
            //        {
            //            throw ex;
            //        }
            //        finally
            //        {
            //            _sync.ExitWriteLock();
            //        }
            //    }
            //    else
            //    {
            //        uts = ser_UserTableSchema.DeserializeXml<UserTableSchema>();
            //    }

            //    uts.TableName = originalUserTableName;
            //    this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.CompactTable(uts);
            //    //here we can start compacting procedure
            //    //return this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.CompactTable(originalUserTableName, uts.CompactedInfoPointer);

            //}
            //catch (System.Exception ex)
            //{
            //    TableCompactingResult cr = new TableCompactingResult();
            //    cr.RealTableName = originalUserTableName;
            //    cr.CompacterException = ex;
            //    this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.TablesCompactingResultHasArrived(cr);
            //    return;
            //}
            //finally
            //{
            //    _sync.ExitUpgradeableReadLock();
            //}


            //using (var tran = this._transactionsCoordinator.GetTransaction())
            //{
            //    try
            //    {
            //        var tr = tran.Select<string, string>(@sys_sts_tables_schemes_stringName, eSelectMode.READ_SYNCHRO, originalUserTableName);
            //        if (!tr.Exists)
            //        {
            //            uts = new UserTableSchema();
            //            uts.CompactedInfoPointer = this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.NewTableCreatedInSchema();
            //            tr.Value = uts.SerializeXml();
            //            tran.Commit();
            //        }
            //        else
            //        {
            //            uts = tr.Value.DeserializeXml<UserTableSchema>();
            //        }
                    
            //    }
            //    catch (Exception ex)
            //    {
            //        TableCompactingResult cr = new TableCompactingResult();
            //        cr.RealTableName = originalUserTableName;
            //        cr.CompacterException = ex;
            //        this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.TablesCompactingResultHasArrived(cr);
            //    }
            //}

            //uts.TableName = originalUserTableName;
            //this._transactionsCoordinator.SchemaCoordinator.StsTablesCompacter.CompactTable(uts);



           
        }





    }
}
